/****************************************************************************
  From the Qt examples
  Modified to do a secure SSL connection
****************************************************************************/

#include <QtGui>
#include <QtNetwork>
#include <QDebug>
#include "client.h"

Client::Client(QWidget *parent) : QDialog(parent)
{
    hostLabel = new QLabel(tr("&Server name:"));
    portLabel = new QLabel(tr("S&erver port:"));

    //Add the line and text edit
    clientTextEdit = new QTextEdit();
    clientLineEdit = new QLineEdit();

    // find out which IP to connect to
    QString ipAddress;
    QList<QHostAddress> ipAddressesList = QNetworkInterface::allAddresses();
    // use the first non-localhost IPv4 address
    for (int i = 0; i < ipAddressesList.size(); ++i) {
        if (ipAddressesList.at(i) != QHostAddress::LocalHost &&
            ipAddressesList.at(i).toIPv4Address()) {
            ipAddress = ipAddressesList.at(i).toString();
            break;
        }
    }
    // if we did not find one, use IPv4 localhost
    if (ipAddress.isEmpty())
        ipAddress = QHostAddress(QHostAddress::LocalHost).toString();

    hostLineEdit = new QLineEdit(ipAddress);
    portLineEdit = new QLineEdit;
    portLineEdit->setValidator(new QIntValidator(1, 65535, this));

    hostLabel->setBuddy(hostLineEdit);
    portLabel->setBuddy(portLineEdit);

    statusLabel = new QLabel(tr("This examples requires that you run the "
                                "Secure Server as well."));

    getFortuneButton = new QPushButton(tr("Connect"));
    getFortuneButton->setDefault(true);
    getFortuneButton->setEnabled(false);

    quitButton = new QPushButton(tr("Quit"));

    buttonBox = new QDialogButtonBox;
    buttonBox->addButton(getFortuneButton, QDialogButtonBox::ActionRole);
    buttonBox->addButton(quitButton, QDialogButtonBox::RejectRole);

    secureSocket = new QSslSocket(this);

    // special slot to handle errors with the certificates
    // in particular the fact that they are self-signed
    connect(secureSocket, SIGNAL(sslErrors(QList<QSslError>)), this,
            SLOT(handleSSLError(QList<QSslError>)));

    connect(hostLineEdit, SIGNAL(textChanged(QString)),
            this, SLOT(enableGetFortuneButton()));
    connect(portLineEdit, SIGNAL(textChanged(QString)),
            this, SLOT(enableGetFortuneButton()));
    connect(getFortuneButton, SIGNAL(clicked()),
            this, SLOT(requestNewFortune()));
    connect(quitButton, SIGNAL(clicked()), this, SLOT(close()));
    connect(secureSocket, SIGNAL(readyRead()), this, SLOT(readData()));
    connect(secureSocket, SIGNAL(error(QAbstractSocket::SocketError)),
            this, SLOT(displayError(QAbstractSocket::SocketError)));
    connect(secureSocket,SIGNAL(disconnected()),this,SLOT(disconnect()));
    connect(secureSocket,SIGNAL(connected()),this,SLOT(connectedToTheServer()));
    connect(clientLineEdit,SIGNAL(returnPressed()),this,SLOT(sendCMsg()));


    //Updated grid
    QGridLayout *mainLayout = new QGridLayout;
    mainLayout->addWidget(clientTextEdit,0,0,1,2);
    mainLayout->addWidget(clientLineEdit,1,0,1,2);
    mainLayout->addWidget(hostLabel, 2, 0);
    mainLayout->addWidget(hostLineEdit, 2, 1);
    mainLayout->addWidget(portLabel, 3, 0);
    mainLayout->addWidget(portLineEdit, 3, 1);
    mainLayout->addWidget(statusLabel, 4, 0, 1, 2);
    mainLayout->addWidget(buttonBox, 5, 0, 1, 2);
    setLayout(mainLayout);

    setWindowTitle(tr("Secure Client"));
    portLineEdit->setFocus();
}

void Client::requestNewFortune()
{
    getFortuneButton->setEnabled(false);
    blockSize = 0;

    qDebug() << "set/reset comminication" << endl;

    secureSocket->abort();
    secureSocket->setPeerVerifyMode(QSslSocket::QueryPeer);
    secureSocket->connectToHostEncrypted(hostLineEdit->text(),
                             portLineEdit->text().toInt());
    if (!secureSocket->waitForEncrypted(1000)) {
         QMessageBox::critical(this, "ERROR", "ERROR: Could not connect to host");
         return;
    }
    displayCertificateWindow();
    statusLabel->setText("Connected to the Server");
    //QSslCertificate peerCertificate = secureSocket->peerCertificate();
    //qDebug() << "Peer Certificate is: " << endl;
    //qDebug() << peerCertificate << endl;
    //QSslCertificate localCertificate = secureSocket->localCertificate();
    //qDebug() << "Local Certificate is: " << endl;
    //qDebug() << localCertificate << endl;
}

QString Client::getCertificateString(const QSslCertificate &cert)
{
    QString certInfo;
    certInfo += "Issuer Org: ";
    certInfo += cert.issuerInfo(QSslCertificate::Organization) + "\n";
    certInfo += "Common Name: ";
    certInfo += cert.issuerInfo(QSslCertificate::CommonName) + "\n";
    certInfo += "Effective Date: ";
    certInfo += cert.effectiveDate().toString() + "\n";
    certInfo += "Expiry Date: ";
    certInfo += cert.expiryDate().toString() + "\n";
    certInfo += "Public Key: ";
    certInfo += cert.publicKey().toPem() + "\n";
    certInfo += "Serial Number: ";
    certInfo += cert.serialNumber() + "\n";
    return certInfo;
}

void Client::displayCertificateWindow()
{
    certificateWindow.setWindowTitle("Certificate Information");
    certificateWindow.setMinimumSize(400,300);
    QString peerCertificateInformation("Peer Certificate Information:\n");
    QSslCertificate peerCertificate = secureSocket->peerCertificate();
    peerCertificateInformation += getCertificateString(peerCertificate);
    certificateWindow.append(peerCertificateInformation);
    QString localCertificateInformation("Local Certificate Information:\n");
    QSslCertificate localCertificate = secureSocket->localCertificate();
    localCertificateInformation += getCertificateString(localCertificate);
    certificateWindow.append(localCertificateInformation);
    certificateWindow.show();
}

void Client::readData()
{
    qDebug() << "Client " << secureSocket->socketDescriptor() << " reading.";
    qDebug() << "Original blockSize: " << blockSize;
    QDataStream in(secureSocket);
    in.setVersion(QDataStream::Qt_4_0);

    if (blockSize == 0) {
        if (secureSocket->bytesAvailable() < (int)sizeof(quint16))
            return;
        in >> blockSize;
    }

    if (secureSocket->bytesAvailable() < blockSize)
        return;

    QString nextFortune;
    in >> nextFortune;

    currentmsg = nextFortune;
    //statusLabel->setText(currentmsg);
    clientTextEdit->append(currentmsg);
    getFortuneButton->setEnabled(true);


    secureSocket->disconnectFromHost();//TESTING
}

void Client::displayError(QAbstractSocket::SocketError socketError)
{
    switch (socketError) {
    case QAbstractSocket::RemoteHostClosedError:
        break;
    case QAbstractSocket::HostNotFoundError:
        QMessageBox::information(this, tr("Fortune Client"),
                                 tr("The host was not found. Please check the "
                                    "host name and port settings."));
        break;
    case QAbstractSocket::ConnectionRefusedError:
        QMessageBox::information(this, tr("Fortune Client"),
                                 tr("The connection was refused by the peer. "
                                    "Make sure the fortune server is running, "
                                    "and check that the host name and port "
                                    "settings are correct."));
        break;
    default:
        QMessageBox::information(this, tr("Fortune Client"),
                                 tr("The following error occurred: %1.")
                                 .arg(secureSocket->errorString()));
    }

    getFortuneButton->setEnabled(true);
}

//The connect button!!
void Client::enableGetFortuneButton()
{
    getFortuneButton->setEnabled(!hostLineEdit->text().isEmpty()
                                 && !portLineEdit->text().isEmpty());
}

// special slot to handle SSL errors...
void Client::handleSSLError(QList<QSslError> errorList)
{
    foreach ( QSslError error, errorList) {
       qDebug() << "Neet to handle SSL error:" << error;
    }
    secureSocket->ignoreSslErrors();
}

void Client::sendCMsg()
{

    qDebug() << "Socket: "  << secureSocket->socketDescriptor();

    QString text = clientLineEdit->text();
    text.prepend("Client> ");
    clientLineEdit->clear();
    clientTextEdit->append(text);
    QByteArray block;
    QDataStream out(&block, QIODevice::WriteOnly);
    out.setVersion(QDataStream::Qt_4_0);
    out << (quint16)0;
    out << text;
    out.device()->seek(0);
    out << (quint16)(block.size() - sizeof(quint16));

    qDebug() << "Client writing: " << text << endl;
    qDebug() << "Block: " << block;
    int test;
    test = secureSocket->write(block);
    if(test == -1)
        qDebug() << "Cant write";
    else
        qDebug() << "Client sent msg..." << endl;
    if(secureSocket->waitForBytesWritten() == true)
        qDebug() << "Written..." << endl;
    else
        qDebug() << "Failed..." << endl;



    secureSocket->disconnectFromHost();


    //tcpSocket->waitForDisconnected();
   // qDebug() << "Disconnected..." << endl;
   //qDebug() << "Written..." << endl;

}

void Client::connectedToTheServer()
{
    qDebug() << "Connected to the SERVER!!!";

}

void Client::disconnect()
{
    qDebug() << "Disconnected from server.";

    requestNewFortune();
}
